Allow guests to register secondary vcpu_time_info
authorKeir Fraser <keir.fraser@citrix.com>
Mon, 19 Oct 2009 10:58:36 +0000 (11:58 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Mon, 19 Oct 2009 10:58:36 +0000 (11:58 +0100)
Allow a guest to register a second location for the VCPU time info
structure for each vcpu.  This is intended to allow the guest kernel
to map this information into a usermode accessible page, so that
usermode can efficiently calculate system time from the TSC without
having to make a syscall.

Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
xen/arch/x86/domain.c
xen/arch/x86/time.c
xen/include/asm-x86/domain.h
xen/include/asm-x86/time.h
xen/include/public/vcpu.h

index d5f216bd26276ad6c27ef61fef4a0d7d83685aed..82ce9754579ce8e9b7e690209083e5c054a5d392 100644 (file)
@@ -962,6 +962,25 @@ arch_do_vcpu_op(
         break;
     }
 
+    case VCPUOP_register_vcpu_time_memory_area:
+    {
+        struct vcpu_register_time_memory_area area;
+
+        rc = -EFAULT;
+        if ( copy_from_guest(&area, arg, 1) )
+            break;
+
+        if ( !guest_handle_okay(area.addr.h, 1) )
+            break;
+
+        rc = 0;
+        v->arch.time_info_guest = area.addr.h;
+
+        force_update_vcpu_system_time(v);
+
+        break;
+    }
+
     case VCPUOP_get_physid:
     {
         struct vcpu_get_physid cpu_id;
index 4a3b64fe925d4b87205ddb41279a575fcc9a71b6..3a0d0db0b63dd5cc9d521cd4af43209ab161b497 100644 (file)
@@ -22,6 +22,7 @@
 #include <xen/irq.h>
 #include <xen/softirq.h>
 #include <xen/keyhandler.h>
+#include <xen/guest_access.h>
 #include <asm/io.h>
 #include <asm/msr.h>
 #include <asm/mpspec.h>
@@ -807,23 +808,15 @@ s_time_t get_s_time(void)
     return now;
 }
 
-static inline void version_update_begin(u32 *version)
-{
-    /* Explicitly OR with 1 just in case version number gets out of sync. */
-    *version = (*version + 1) | 1;
-    wmb();
-}
-
-static inline void version_update_end(u32 *version)
-{
-    wmb();
-    (*version)++;
-}
+/* Explicitly OR with 1 just in case version number gets out of sync. */
+#define version_update_begin(v) (((v)+1)|1)
+#define version_update_end(v)   ((v)+1)
 
-void update_vcpu_system_time(struct vcpu *v)
+static void __update_vcpu_system_time(struct vcpu *v, int force)
 {
     struct cpu_time       *t;
-    struct vcpu_time_info *u;
+    struct vcpu_time_info *u, _u;
+    XEN_GUEST_HANDLE(vcpu_time_info_t) user_u;
 
     if ( v->vcpu_info == NULL )
         return;
@@ -831,35 +824,79 @@ void update_vcpu_system_time(struct vcpu *v)
     t = &this_cpu(cpu_time);
     u = &vcpu_info(v, time);
 
+    /* Don't bother unless timestamps have changed or we are forced. */
+    if ( !force && (u->tsc_timestamp == (v->domain->arch.vtsc
+                                         ? t->stime_local_stamp
+                                         : t->local_tsc_stamp)) )
+        return;
+
+    memset(&_u, 0, sizeof(_u));
+
     if ( v->domain->arch.vtsc )
     {
-        if ( u->tsc_timestamp == t->stime_local_stamp )
-            return;
-        version_update_begin(&u->version);
-        u->tsc_timestamp     = t->stime_local_stamp;
-        u->system_time       = t->stime_local_stamp;
-        u->tsc_to_system_mul = 0x80000000u;
-        u->tsc_shift         = 1;
-        version_update_end(&u->version);
+        _u.tsc_timestamp     = t->stime_local_stamp;
+        _u.system_time       = t->stime_local_stamp;
+        _u.tsc_to_system_mul = 0x80000000u;
+        _u.tsc_shift         = 1;
+    }
+    else
+    {
+        _u.tsc_timestamp     = t->local_tsc_stamp;
+        _u.system_time       = t->stime_local_stamp;
+        _u.tsc_to_system_mul = t->tsc_scale.mul_frac;
+        _u.tsc_shift         = (s8)t->tsc_scale.shift;
     }
-    else if ( u->tsc_timestamp != t->local_tsc_stamp )
+
+    /* 1. Update guest kernel version. */
+    _u.version = u->version = version_update_begin(u->version);
+    wmb();
+    /* 2. Update all other guest kernel fields. */
+    *u = _u;
+    wmb();
+    /* 3. Update guest kernel version. */
+    u->version = version_update_end(u->version);
+
+    user_u = v->arch.time_info_guest;
+    if ( !guest_handle_is_null(user_u) )
     {
-        version_update_begin(&u->version);
-        u->tsc_timestamp     = t->local_tsc_stamp;
-        u->system_time       = t->stime_local_stamp;
-        u->tsc_to_system_mul = t->tsc_scale.mul_frac;
-        u->tsc_shift         = (s8)t->tsc_scale.shift;
-        version_update_end(&u->version);
+        /* 1. Update userspace version. */
+        __copy_field_to_guest(user_u, &_u, version);
+        wmb();
+        /* 2. Update all other userspavce fields. */
+        __copy_to_guest(user_u, &_u, 1);
+        wmb();
+        /* 3. Update userspace version. */
+        _u.version = version_update_end(_u.version);
+        __copy_field_to_guest(user_u, &_u, version);
     }
 }
 
+void update_vcpu_system_time(struct vcpu *v)
+{
+    __update_vcpu_system_time(v, 0);
+}
+
+void force_update_vcpu_system_time(struct vcpu *v)
+{
+    __update_vcpu_system_time(v, 1);
+}
+
 void update_domain_wallclock_time(struct domain *d)
 {
+    uint32_t *wc_version;
+
     spin_lock(&wc_lock);
-    version_update_begin(&shared_info(d, wc_version));
+
+    wc_version = &shared_info(d, wc_version);
+    *wc_version = version_update_begin(*wc_version);
+    wmb();
+
     shared_info(d, wc_sec)  = wc_sec + d->time_offset_seconds;
     shared_info(d, wc_nsec) = wc_nsec;
-    version_update_end(&shared_info(d, wc_version));
+
+    wmb();
+    *wc_version = version_update_end(*wc_version);
+
     spin_unlock(&wc_lock);
 }
 
index 1023c9596528e81b9e7ad03465e455691c4a8c41..08f2696ba96a0a542dde112cc7604e774f27df5a 100644 (file)
@@ -6,6 +6,7 @@
 #include <asm/hvm/vcpu.h>
 #include <asm/hvm/domain.h>
 #include <asm/e820.h>
+#include <public/vcpu.h>
 
 #define has_32bit_shinfo(d)    ((d)->arch.has_32bit_shinfo)
 #define is_pv_32bit_domain(d)  ((d)->arch.is_32bit_pv)
@@ -418,6 +419,9 @@ struct arch_vcpu
     uint32_t gdbsx_vcpu_event;
 #endif 
 
+    /* A secondary copy of the vcpu time info. */
+    XEN_GUEST_HANDLE(vcpu_time_info_t) time_info_guest;
+
 } __cacheline_aligned;
 
 /* Shorthands to improve code legibility. */
index dac53bcc2599a0f983db44501d96dee5aa30ef81..c72cb8e50652732dac4f966889ed8f930ca636d0 100644 (file)
@@ -43,4 +43,6 @@ uint64_t ns_to_acpi_pm_tick(uint64_t ns);
 
 void pv_soft_rdtsc(struct vcpu *v, struct cpu_user_regs *regs);
 
+void force_update_vcpu_system_time(struct vcpu *v);
+
 #endif /* __X86_TIME_H__ */
index f592036f230d3ce436259212c178f6de4d896e6a..c98e6be0e1635fae482a519b28d82f09022b8f5b 100644 (file)
@@ -202,6 +202,34 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_get_physid_t);
 #define xen_vcpu_physid_to_x86_acpiid(physid) \
     ((((uint32_t)((physid)>>32)) >= 0xff) ? 0xff : ((uint8_t)((physid)>>32)))
 
+/* 
+ * Register a memory location to get a secondary copy of the vcpu time
+ * parameters.  The master copy still exists as part of the vcpu shared
+ * memory area, and this secondary copy is updated whenever the master copy
+ * is updated (and using the same versioning scheme for synchronisation).
+ *
+ * The intent is that this copy may be mapped (RO) into userspace so
+ * that usermode can compute system time using the time info and the
+ * tsc.  Usermode will see an array of vcpu_time_info structures, one
+ * for each vcpu, and choose the right one by an existing mechanism
+ * which allows it to get the current vcpu number (such as via a
+ * segment limit).  It can then apply the normal algorithm to compute
+ * system time from the tsc.
+ *
+ * @extra_arg == pointer to vcpu_register_time_info_memory_area structure.
+ */
+#define VCPUOP_register_vcpu_time_memory_area   13
+DEFINE_XEN_GUEST_HANDLE(vcpu_time_info_t);
+struct vcpu_register_time_memory_area {
+    union {
+        XEN_GUEST_HANDLE(vcpu_time_info_t) h;
+        struct vcpu_time_info *v;
+        uint64_t p;
+    } addr;
+};
+typedef struct vcpu_register_time_memory_area vcpu_register_time_memory_area_t;
+DEFINE_XEN_GUEST_HANDLE(vcpu_register_time_memory_area_t);
+
 #endif /* __XEN_PUBLIC_VCPU_H__ */
 
 /*